// $Id: node.cpp 66 2004-11-06 13:09:34Z jonas $
#include <algorithm>
#include "node.h"
#include "storedfile.h"
#include "transfer.h"
#include "loghandler.h"
using namespace std;

#include <iostream>

FileHandler *Node::m_fileHandler = 0;
unsigned int Node::m_nextSearchGuid = 0;
unsigned int Transfer::m_nextGuid = 0;

bool Node::iOne = false;	// DEBUG
bool Node::iTwo = false;	// DEBUG

/**
 * Init all members.
 *
 * @param bandwidthUp Upload bandwidth in bytes per timestep (1 time step is in
 *  seconds and can be obtained from Clock::m_secondsPerTimeStep)
 * @param bandwidthDown Download bandwidth in bytes per timestep.
 * @param routingTableSize Number of entries in the routing table.
 * @param fileStoreSize Number of files to fit in the file store.
 * @param requestHopsToLive The HTL to use on requests.
 * @param insertHopsToLive The HTL to use on inserts.
 * @param requestProbability The probability for one node to request a file at
 *  one time step.
 * @param insertProbability The probability for one node to insert a file at
 *  one time step.
 * @param fileHandler A pointer to the file handler to use.
 */
Node::Node(unsigned int bandwidthUp, unsigned int bandwidthDown, unsigned int routingTableSize, unsigned int fileStoreSize, unsigned int requestHopsToLive, unsigned int insertHopsToLive, float requestProbability, float insertProbability, FileHandler *fileHandler)
	: m_routingTableSize(routingTableSize), m_fileStoreSize(fileStoreSize), m_requestHopsToLive(requestHopsToLive), m_insertHopsToLive(insertHopsToLive), m_requestProbability(requestProbability), m_insertProbability(insertProbability), m_bandwidthUp(bandwidthUp), m_bandwidthDown(bandwidthDown)
{
	if (!m_fileHandler) {
		m_fileHandler = fileHandler;
	}
}

/**
 * @todo Write documentation
 */
Node::~Node()
{
	// Free filestore memory (the StoredFile objects)
	for (map<File *, StoredFile *>::iterator file=m_fileStore.begin(); file!=m_fileStore.end(); file++) {
		delete (*file).second;
	}
	m_fileStore.clear();

	// Free transfer objects memory
	for (unsigned int i=0; i<m_transfers.size(); i++) {
		delete m_transfers[i];
	}
	m_transfers.clear();
}

/**
 * Adds a neighbor to the noded routing table
 * @todo Does NOT respect cache size! Return false if full? Subclasses should
 *  implement!
 *
 * @return Returns true if the node was added to the list. False otherwise (if
 *  the node already is in the list).
 */
/*bool Node::addNeighbor(Node *neighbor)
{
	if (find(m_routingTable.begin(), m_routingTable.end(), neighbor) == m_routingTable.end()) {
		// TODO: Optimize this insert by sorting by Id or something? Or just general optimization? How?
		m_routingTable.push_back(neighbor);
		return true;
	}
	return false;
}*/

/**
 * @todo Jonas should tell HE to document this soon...
 */
StoredFile *Node::getStoredFile(File *file)
{
	//return m_fileStore[file];

	// Search for the file
	map<File *, StoredFile *>::iterator iter = m_fileStore.find(file);
	if (iter != m_fileStore.end()) {
		(*iter).second->touch();
		return (*iter).second;
	}
	// The file was not found.
	return 0;
}

/**
 * @todo Document this...
 *
 * @param file
 * @param hopsToLive
 * @returns True if the file was inserted ok and false otherwise.
 */
bool Node::insertFile(File *file, int hopsToLive)
{
	NodeRequestFileParameters params(m_nextSearchGuid++, hopsToLive, file, true);
	params.m_fileOwner = this;

	bool res = routeFile(this, &params);
	LogHandler::getInstance()->node_requestFile(this, &params, res);
	return !res;
}

/**
 * @todo Write documentation
 */
bool Node::requestFile(File *file, int hopsToLive)
{
	NodeRequestFileParameters params(m_nextSearchGuid++, hopsToLive, file, false);

	bool res = routeFile(this, &params);
	LogHandler::getInstance()->node_requestFile(this, &params, res);
	return res;
}

/**
 * Nodes consult this centralized method in order to find out if they should
 * request a file from the network.
 *
 * @returns Whether a node should request a file or not
 */
bool Node::doRequest()
{
//	 if(Clock::getInstance()->getTime()==20){	// DEBUG
//		 return true;	// DEBUG
//	 }	// DEBUG

	if (m_requestProbability > (rand()/(double)RAND_MAX)) {
		return true;
	}
	return false;
}

/**
 * Nodes consult this centralized method in order to find out if they should
 * insert a file into the network.
 *
 * @returns Whether a node should insert a file or not
 */
bool Node::doInsert()
{
//	if(Clock::getInstance()->getTime()==10 && !iOne){	// DEBUG
//		return iOne = true;	// DEBUG
//	}	// DEBUG

//	if(Clock::getInstance()->getTime()==20 && !iTwo){	// DEBUG
//		return iTwo = true;	// DEBUG
//	}	// DEBUG

	if (m_insertProbability > (rand()/(double)RAND_MAX)) {
		return true;
	}
	return false;
}

/**
 * Get a pointer to one of the active transfers.
 *
 * @param index An index (starting with 0) specifying which transfer to get. An
 *  index too large will return a null pointer.
 * @returns A pointer to a transfer (or 0 if the transfer index was too large)
 */
Transfer *Node::getTransfer(int index)
{
	if ((unsigned int) index >= m_transfers.size()) {
		return 0;
	}
	return m_transfers[index];
}

/**
 * @todo Document this!
 */
Transfer *Node::getTransfer(StoredFile *storedFile)
{
	for (vector<Transfer *>::iterator transfer=m_transfers.begin(); transfer!=m_transfers.end(); transfer++) {
		if ((*transfer)->m_file == storedFile) {
			return *transfer;
		}
	}
	return 0;
}

/**
 * @return The number of active transfers.
 */
int Node::getTransferCount()
{
	return m_transfers.size();
}

/**
 * @return Documentation goes here...
 */
unsigned int Node::getBandwidthUp()
{
	return m_bandwidthUp;
}

/**
 * @return Documentation goes here...
 */
unsigned int Node::getBandwidthDown()
{
 	return m_bandwidthDown;
}

/**
 * @return Documentation goes here...
 */
float Node::getInsertProbability()
{
	return m_insertProbability;
}

/**
 * @return Documentation goes here...
 */
float Node::getRequestProbability()
{
	return m_requestProbability;
}

/**
 * @return Documentation goes here...
 */
void Node::setInsertProbability(float insertProbability)
{
	m_insertProbability = insertProbability;
}

/**
 * @return Documentation goes here...
 */
void Node::setRequestProbability(float requestProbability)
{
	m_requestProbability = requestProbability;
}
